﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net;

namespace Innovox.YDeviceLib
{
    public enum Y1gOptions
    {
        Unknown,
        NoneMotion,
        Motion
    }

    public class Y1G : YDevice
    {
        #region Constructors

        public Y1G(string ip): base(ip)
        {
            InitProperties();
        }

        public Y1G(IPAddress ip): base(ip)
        {
            InitProperties();
        }

        #endregion Constructors

        #region Properties
        public bool TestInProgress { get; private set; }
        public bool TestPassed
        {
            get
            {//ZcdTestPassed &&
                if (Option == Y1gOptions.Motion)
                    return ( PirSensTestPassed && LumSensTestPassed && OffDelayTestPassed
                         && NetSwitchTestPassed && ButtonTestPassed && SwitchStatusTestPassed
                         && LumTestPassed && RxTestPassed && TxTestPassed);
                else if (Option == Y1gOptions.NoneMotion)
                    return ( ButtonTestPassed && SwitchStatusTestPassed & NetSwitchTestPassed
                          && RxTestPassed && TxTestPassed);
                else
                    return false;
            }
            
        }

        public Y1gOptions Option { get; private set; }
        public bool NetworkOn { get; private set; }        
        public string SwitchStatus { get; private set; }
        public bool ButtonPressed { get; private set; }
        public int PirSens { get; private set; }
        public int OffDelay { get; private set; }
        public int LumSens { get; private set; }
        public int Luminosity { get; private set; }
        public int NoMotionTime { get; private set; }
        public int ZcdFreq { get; private set; }
        
        #endregion Properties

        #region Helper functions

        public void ReadY1gConfig()
        {
            string[] uris = new string[] {  "/obix/y1g/adv/ledColor/", "/obix/y1g/adv/flickReps/", "/obix/name/", "/obix/location/"};
            BatchOutObjects = Read(uris);
            
            if (Option == Y1gOptions.Motion)
            {
                if (BatchOutObjects != null)
                {
                    string error = ErrorMessage;
                    uris = new string[] { "/obix/y1g/pirSens/", "/obix/y1g/offDelay/", "/obix/y1g/autoMode/",
                                          "/obix/y1g/adv/motionMuteDelay/", "/obix/y1g/adv/lumFactor/" };

                List<ObixObject> objs = Read(uris);

                    if (objs != null)
                        BatchOutObjects.AddRange(objs);

                    if (ErrorMessage.Contains("PIR is not installed"))
                        ErrorMessage = error;
                    else
                        ErrorMessage = error + ErrorMessage;
                }
            }
        }

        public void ReadY1gData()
        {
            string[] uris = new string[] { "/obix/y1g/adv/switchStatus/", "/obix/y1g/adv/networkOn/", "/obix/y1g/bPressLapse/", "/obix/y1g/latchActive/" };

            BatchOutObjects = Read(uris);

            if (Option == Y1gOptions.Motion)
            {
                if (BatchOutObjects != null)
                {
                    string error = ErrorMessage;

                    uris = new string[] { "/obix/y1g/adv/luminosity/", "/obix/y1g/noMotionTime/" };
                    Thread.Sleep(200);
                    List<ObixObject> objs = Read(uris);

                    if (objs != null)
                        BatchOutObjects.AddRange(objs);

                    if (ErrorMessage.Contains("PIR is not installed"))
                        ErrorMessage = error;
                    else
                        ErrorMessage = error + ErrorMessage;
                }
            }
        }
      //     public void BeginWrite(List<YDevice> devices, ObixEntry[] entries)
        static public void WriteMacAddress(List<YDevice> devices, ObixEntry[] entries)
        {
            List<ObixEntry> noMotionEntryList = new List<ObixEntry>();
            foreach (ObixEntry oe in entries)
            {
                if (oe.Uri == "/obix/macAddress/"  )
                    continue;
                else
                    noMotionEntryList.Add(oe);
            }

            ObixEntry[] noMotionEntries = noMotionEntryList.ToArray();

            foreach (Y1G y in devices)
            {
                if (y.Option == Y1gOptions.Motion)
                    y.ObixEntries = entries;
                else
                    y.ObixEntries = noMotionEntries;
            }
        }


//        public void BeginWrite(List<YDevice> devices, ObixEntry[] entries)
        static public void WriteNvParamEntries(List<YDevice> devices, ObixEntry[] entries)
        {
            List<ObixEntry> noMotionEntryList = new List<ObixEntry>();
            foreach (ObixEntry oe in entries)
            {
                if (oe.Uri == "/obix/y1g/pirSens/" || oe.Uri == "/obix/y1g/offDelay/" || oe.Uri == "/obix/y1g/autoMode/"
                    || oe.Uri == "/obix/y1g/adv/motionMuteDelay/" || oe.Uri == "/obix/y1g/adv/lumFactor/" )
                    continue;
                else
                    noMotionEntryList.Add(oe);
            }

            ObixEntry[] noMotionEntries = noMotionEntryList.ToArray();

            foreach (Y1G y in devices)
            {
                if (y.Option == Y1gOptions.Motion)
                    y.ObixEntries = entries;
                else
                    y.ObixEntries = noMotionEntries;
            }
        }

        private void InitProperties()
        {
            TestInProgress = false;
            //TestPassed = false;
            DetectMotion();
            NetworkOn = false;
        }

        #endregion Helper functions

        #region Hardware Test functions

        const int _pirSensMinTestValue = 55;
        const int _pirSensMaxTestValue = 75;
        const int _LumSensMinTestValue = 378;
        const int _lumSensMaxTestValue = 578;
        const int _offDelayMinTestValue = 685;
        const int _offDelayMaxTestValue = 1085;
        const int _luxDeltaTestValue = 30;
        const int _luxMinDeltaTestValue = 3;
        const int _luxStableCountTestValue = 3;
        const int _noMotionMinCountTestValue = 6;

        public bool PirSensTestPassed;
        public bool LumSensTestPassed;
        public bool LumTestPassed;
        bool _lumDeltaTestPassed;
        bool _lumStaticTestPassed;
        public bool OffDelayTestPassed;
        public bool PirTestPassed;
        public bool NetSwitchTestPassed;
        public bool SwitchStatusTestPassed;
        public bool ButtonTestPassed;
        public bool ZcdTestPassed;
        public bool RxTestPassed;
        public bool TxTestPassed;

        int _previousLuminosity;
        //int _previousNoMotionCount;
        int _MotionTestState;
        bool _previousNetSwitchState;
        int _luxStaticCount;
        int _switchStatusBits;
        bool _previousButtonState;

        public bool Test()
        {
            if (RefreshProperties() == true)
            {
                TestButtonAndSwitch();
                TestMotion();
                return true;
            }
            return false;
        }

        public void ResetTest()
        {
            TestInProgress = true;
            PirSensTestPassed = false;
            LumSensTestPassed = false;
            OffDelayTestPassed = false;
            NetSwitchTestPassed = false;
            ButtonTestPassed = false;
            ZcdTestPassed = false;
            SwitchStatusTestPassed = false;
            _lumDeltaTestPassed = false;
            _lumStaticTestPassed = false;
            LumTestPassed = false;
            RxTestPassed = false;
            TxTestPassed = false;
            PirTestPassed = false;

            _previousLuminosity = Luminosity;
           // _previousNoMotionCount = -1;
            _MotionTestState = 0;
            _previousNetSwitchState = true;
            _previousButtonState = false;
            _switchStatusBits = 0;
            _luxStaticCount = 0;

            //Modem.RxBitrate = 0;
            //Modem.TxBitrate = 0;
            NoMotionTime = 0;
            PirSens = 0;
            OffDelay = 0;
            LumSens = 0;
            Luminosity = 0;
            Option = Y1gOptions.Unknown;

        }

        private void TestButtonAndSwitch()
        {
            if (_previousNetSwitchState == false && NetworkOn == true)
                NetSwitchTestPassed = true;
            
            _previousNetSwitchState = NetworkOn;

            if (_previousButtonState == true && ButtonPressed == false)
                ButtonTestPassed = true;

            _previousButtonState = ButtonPressed;

            if (SwitchStatus == "on" || SwitchStatus == "auto")
                _switchStatusBits |= 1;
            else if( SwitchStatus == "off")
                _switchStatusBits |= 2;
            else if (SwitchStatus == "overrride" || SwitchStatus == "ovr")
                _switchStatusBits |= 4;

            if (_switchStatusBits == 7 && (SwitchStatus == "on" || SwitchStatus == "auto"))
                SwitchStatusTestPassed = true;
            else
                SwitchStatusTestPassed = false;

            if (Modem.RxBitrate >= 10)
                RxTestPassed = true;
            else
                RxTestPassed = false;

            if (Modem.TxBitrate >= 10)
                TxTestPassed = true;
            else
                TxTestPassed = false;

        }

        private void TestMotion()
        {
            if (NetworkOn == true)
            {
                if (NetSwitchTestPassed == true)
                {
                    // test pots
                    if (PirSens >= _pirSensMinTestValue && PirSens <= _pirSensMaxTestValue)
                        PirSensTestPassed = true;
                    else
                        PirSensTestPassed = false;

                    if (LumSens >= _LumSensMinTestValue && LumSens <= _lumSensMaxTestValue)
                        LumSensTestPassed = true;
                    else
                        LumSensTestPassed = false;

                    if (OffDelay >= _offDelayMinTestValue && OffDelay <= _offDelayMaxTestValue)
                        OffDelayTestPassed = true;
                    else
                        OffDelayTestPassed = false;
                }

                // test lum sensor
                if (_lumStaticTestPassed && (Luminosity - _previousLuminosity) > _luxDeltaTestValue)
                    _lumDeltaTestPassed = true;
                if (Math.Abs((Luminosity - _previousLuminosity)) < _luxMinDeltaTestValue)
                {
                    _luxStaticCount++;
                    if (_luxStaticCount > _luxStableCountTestValue)
                        _lumStaticTestPassed = true;
                }
                else
                    _luxStaticCount = 0;
                _previousLuminosity = Luminosity;
                LumTestPassed = _lumDeltaTestPassed & _lumStaticTestPassed;

                // test motion detector
                //if (_previousNoMotionCount == -1)
                //    _previousNoMotionCount = NoMotionTime;
                switch (_MotionTestState)
                {
                    case 0:
                        if (NoMotionTime <= 2)
                            _MotionTestState++;
                        break;
                    case 1:
                        if (NoMotionTime >= _noMotionMinCountTestValue)
                            _MotionTestState++;
                        break;
                    case 2:
                        if (NoMotionTime < _noMotionMinCountTestValue)
                        {
                            PirTestPassed = true;
                            _MotionTestState++;
                        }
                        break;
                    case 3:
                        break;
                }


                //if ((_previousNoMotionCount >= _noMotionMinCountTestValue) && (NoMotionTime < _noMotionMinCountTestValue))
                //    PirTestPassed = true;
                //_previousNoMotionCount = NoMotionTime;
            }
        }

        private void DetectMotion()
        {
            try
            {
                string[] uris = new string[] { "/obix/y1g/pirSens/" };

                BatchOutObjects = Read(uris);

                if (ErrorMessage.Contains("PIR is not installed"))
                    Option = Y1gOptions.NoneMotion;
                else if (BatchOutObjects == null)
                    Option = Y1gOptions.Unknown;
                else
                {
                    string val = Value("/obix/y1g/pirSens/");
                    if (val != null && val != "" && ErrorMessage == "")
                    {
                        int sens;
                        if (int.TryParse(val, out sens) == true)
                        {
                            Option = Y1gOptions.Motion;
                            return;
                        }
                    }
                    Option = Y1gOptions.Unknown;
                }
            }
            catch
            {
                Option = Y1gOptions.Unknown;
            }
        }

        public bool RefreshProperties()
        {
            string[] uris;

            if (Modem.UpdateInfo() == false)
                return false;

            if( Option == Y1gOptions.Unknown || Option == Y1gOptions.Motion)
                uris = new string[] {  "/obix/y1g/adv/networkOn/", "/obix/y1g/adv/switchStatus/", "/obix/y1g/adv/lumSens/",
                                            "/obix/y1g/pirSens/", "/obix/y1g/offDelay/", "/obix/y1g/adv/luminosity/",
                                            "/obix/y1g/noMotionTime/","/obix/y1g/adv/buttonPressed/","/obix/status/zcdFreq/"};
            else
                uris = new string[] { "/obix/y1g/adv/networkOn/", "/obix/y1g/adv/switchStatus/", "/obix/y1g/adv/buttonPressed/", "/obix/status/zcdFreq/" };

            BatchOutObjects = Read(uris);
            if (BatchOutObjects == null)
                return false;

            int freq;
            string val = Value("/obix/status/zcdFreq/");
            if (int.TryParse(val, out freq))
            {
                if (freq >= 57 && freq <= 63)
                {
                    ZcdTestPassed = true;
                    ZcdFreq = freq;
                }
            }

            val = Value("/obix/y1g/adv/networkOn/");
            bool netSwitch;

            if (bool.TryParse(val, out netSwitch) == true)
            {                
                NetworkOn = netSwitch;
                val = Value("/obix/y1g/adv/switchStatus/");

                if (val != null)
                    SwitchStatus = val;

                val = Value("/obix/y1g/adv/buttonPressed/");
                bool buttonPressed;
                if (bool.TryParse(val, out buttonPressed) == true)
                    ButtonPressed = buttonPressed;
                
                if (NetworkOn == true && (Option == Y1gOptions.Unknown || Option == Y1gOptions.Motion))
                {
                    int i;
                    val = Value("/obix/y1g/pirSens/");
                    if (int.TryParse(val, out i))
                    {
                        PirSens = i;
                        if (Option == Y1gOptions.Unknown)
                            Option = Y1gOptions.Motion;
                    }
                    else
                        Option = Y1gOptions.NoneMotion;

                    if (int.TryParse(Value("/obix/y1g/offDelay/"), out i))
                        OffDelay = i;

                    if (int.TryParse(Value("/obix/y1g/adv/lumSens/"), out i))
                        LumSens = i;

                    if (int.TryParse(Value("/obix/y1g/adv/luminosity/"), out i))
                        Luminosity = i;

                    if (int.TryParse(Value("/obix/y1g/noMotionTime/"), out i))
                        NoMotionTime = i;
                }
            }
            return true;
        }

        #endregion Hardware Test functions
    }
}
